﻿using System.Text;
using System;

namespace FuzzScript
{
    /// <summary>
    /// Implementation of a Knowledgebase Node. The Knowledgebase holds all the system variables and their fuzzy sets
    /// </summary>
    class KnowledgeBaseNode : Node
    {
        /// <summary>
        /// Constructor-Initializes system to two nodes
        /// </summary>
        internal KnowledgeBaseNode()
        {
            // two siblings - input and output variables
            base.AddSibling(null);
            base.AddSibling(null);
        }

        /// <summary>
        /// Throws Exception - Siblings can only be manages using the InputVariables and
        /// OutputVariables properties
        /// </summary>
        /// <param name="p_SiblingNode"></param>
        public override void AddSibling(INode p_SiblingNode)
        {
            throw new Exception("Error - Attempt to add a sibling node to a Knowledgebase node");
        }

        /// <summary>
        /// Sets or Gets the input variables node
        /// </summary>
        public INode InputVariables
        {
            get { return m_SiblingNodes[0]; }
            set { m_SiblingNodes[0] = value; }
        }

        /// <summary>
        /// Sets or Gets the output variables node
        /// </summary>
        public INode OutputVariables
        {
            get { return m_SiblingNodes[1]; }
            set { m_SiblingNodes[1] = value; }
        }

        /// <summary>
        /// Returns the c# source code that will be used bu the optimized system
        /// </summary>
        public override string Generate()
        {
            // main string builder: 2 others are used to prevent looping many times
            StringBuilder sCode = new StringBuilder();
            
            //string builder for fuzzification method
            StringBuilder sFuzzificationMethod = new StringBuilder();
            
            // string builder for the control method
            StringBuilder sMainControlMethod = new StringBuilder();

            // used in fuzzification.
            sMainControlMethod.Append("int iDOM;\n");
            
            
            sMainControlMethod.Append("\nExecutePredicates();\n\n");

            foreach (INode node in InputVariables.Siblings)
            {
                // if this is an input variable, the following attributes are added: 
                // double SetNameDOM;
                // for each set of this variable

                // in addition a method is created that carries out the fuzzification process 
                // for this variable. The entries for the method are stored in a second stringbuilder
                // object, sFuzzificationMethod

                // clear sFuzzificationMethod
                sFuzzificationMethod.Remove(0, sFuzzificationMethod.Length);

                foreach (INode set in node.Siblings)
                {
                    // create the fuzzification variable
                    sCode.Append("\ndouble " + SymbolTable.AttributeSetDOM(node.Name,
                        set.Name) + ";\n");

                    // create the entry for the fuzzification method
                    sFuzzificationMethod.Append(SymbolTable.AttributeSetDOM(node.Name,
                         set.Name) + " = ");
                    sFuzzificationMethod.Append(SymbolTable.AttributeFuzzySet(node.Name,
                        set.Name)
                        + "[p_iDom];\n");
                }


                // a method performing the fuzzification function, given an input value 
                // the input value is actually an index to the input value
                sCode.Append("private void " + SymbolTable.MethodSetVariableDOMMethod(node.Name) + "(int p_iDom){\n");
                {
                    sCode.Append(sFuzzificationMethod);
                }
                sCode.Append("}\n\n");

                sCode.Append(node.Generate());


                sMainControlMethod.Append("iDOM = VariableToResolution(p_InternalState[\""
                    + node.Name + "\"]," +
                    SymbolTable.AttributeVariableUpperLimit(node.Name) + "," +
                    SymbolTable.AttributeVariableLowerLimit(node.Name)
                    + ");\n");
                sMainControlMethod.Append(SymbolTable.MethodSetVariableDOMMethod(node.Name) + "(iDOM);\n");

            }

            sMainControlMethod.Append("\nExecuteRuleBase();\n");

            foreach (INode node in OutputVariables.Siblings)
            {
                // if this is an output variable, add an output set, that will contain
                // the fuzzy output value of the variable and will be used for defuzzification
                sCode.Append(
                    "double[] "
                    + SymbolTable.AttributeOutputVariableSet(node.Name)
                    + " = new double [" + FuzzyVariable.Resolution + "];\n");


                sCode.Append(node.Generate());


                sMainControlMethod.Append("if(p_ControlValues.ContainsKey(\"" + node.Name + "\"))\n{");

                sMainControlMethod.Append("p_ControlValues[\"" + node.Name + "\"" + @"]=  
                    ResolutionToVariable(
                    CoGDefuzzification(" + SymbolTable.AttributeOutputVariableSet(node.Name) + @"),
                    " + SymbolTable.AttributeVariableUpperLimit(node.Name) + @",
                    " + SymbolTable.AttributeVariableLowerLimit(node.Name) + @"
                    );");

                sMainControlMethod.Append("}\n");
            }


            // main control function
            sCode.Append("\n\npublic void Control(Dictionary<string, double> p_InternalState, Dictionary<string, double> p_ControlValues)\n");
            sCode.Append("{\n");
            sCode.Append(sMainControlMethod);
            sCode.Append("}");

            return sCode.ToString();
        }
    }
}
